package com.zt.chatgpt.types.sdk.wexin.util;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.util.*;

/**
 * @author: zt
 * @Description: xml 工具类
 * @create: 2024-05-18 22:40
 */

public class XmlUtil {

    /**
     * 将XML格式的数据转换为Map形式。
     *
     * @param request HttpServletRequest对象，用于获取客户端上传的XML数据的输入流。
     * @return 返回一个包含XML数据键值对的Map对象。
     * @throws IOException 当读取输入流或关闭输入流时发生I/O错误。
     * @throws DocumentException 当解析XML文档时发生错误。
     */
    public static Map<String, String> xmlToMap(HttpServletRequest request) throws IOException, DocumentException {
        try (InputStream inputStream = request.getInputStream()){
            // 1. 信息保存在map中
            HashMap<String, String> res = new HashMap<>();
            // 2. 读取输入流获取xml文档对象
            SAXReader reader = new SAXReader();
            Document document = reader.read(inputStream);
            // 3. 得到xml根元素
            Element root = document.getRootElement();
            // 4. 获取根元素下的下的子节点
            List<Element> elementList = root.elements();
            // 5. 读取子节点数据
            for(Element e : elementList) {
                res.put(e.getName(), e.getText());
            }
            // 6. 释放资源
            inputStream.close();
            return res;
        }
    }

    /**
     * 将给定的map转换成XML格式的字符串，用于响应微信服务器请求。
     *
     * @param map 包含要转换成XML格式的数据的键值对。
     * @return 生成的XML字符串。如果在生成过程中发生异常，则返回null。
     */
    static String mapToXML(Map map) {
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        mapToXML2(map, sb);
        sb.append("</xml>");
        try {
            return sb.toString();
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 将Map对象映射为XML格式的字符串。
     * @param map 需要转换为XML格式的Map对象，其中键值对代表XML的元素及其内容。
     * @param sb 用于累积生成的XML字符串的StringBuffer对象。
     */
    private static void mapToXML2(Map map, StringBuffer sb) {
        // 获取map的所有键
        Set set = map.keySet();
        for (Object o : set) {
            String key = (String) o;
            Object value = map.get(key);
            // 如果值为null，则将其置为空字符串
            if (null == value) {
                value = "";
            }
            // 检查值是否为ArrayList类型
            if (value.getClass().getName().equals("java.util.ArrayList")) {
                ArrayList list = (ArrayList) map.get(key);
                // 开始写入ArrayList元素的XML结构
                sb.append("<").append(key).append(">");
                // 遍历ArrayList，递归转换每个元素为XML格式
                for (Object o1 : list) {
                    HashMap hm = (HashMap) o1;
                    mapToXML2(hm, sb);
                }
                // 结束ArrayList元素的XML结构
                sb.append("</").append(key).append(">");

            } else {
                // 处理值为HashMap的情况
                if (value instanceof HashMap) {
                    sb.append("<").append(key).append(">");
                    // 递归转换HashMap为XML格式
                    mapToXML2((HashMap) value, sb);
                    sb.append("</").append(key).append(">");
                } else {
                    // 处理值为普通字符串或其他类型的情况
                    sb.append("<").append(key).append("><![CDATA[").append(value).append("]]></").append(key).append(">");
                }

            }

        }
    }


    public static XStream getMyXStream() {
        return new XStream(new XppDriver() {
            @Override
            public HierarchicalStreamWriter createWriter(Writer out) {
                return new PrettyPrintWriter(out) {
                    // 对所有xml节点都增加CDATA标记
                    boolean cdata = true;

                    @Override
                    public void startNode(String name, Class clazz) {
                        super.startNode(name, clazz);
                    }

                    @Override
                    protected void writeText(QuickWriter writer, String text) {
                        if (cdata && !StringUtils.isNumeric(text)) {
                            writer.write("<![CDATA[");
                            writer.write(text);
                            writer.write("]]>");
                        } else {
                            writer.write(text);
                        }
                    }
                };
            }
        });
    }

    /**
     * bean转成微信的xml消息格式
     */
    public static String beanToXml(Object object) {
        XStream xStream = getMyXStream();
        xStream.alias("xml", object.getClass());
        xStream.processAnnotations(object.getClass());
        String xml = xStream.toXML(object);
        if (!StringUtils.isEmpty(xml)) {
            return xml;
        } else {
            return null;
        }
    }

    /**
     * xml转成bean泛型方法
     */
    public static <T> T xmlToBean(String resultXml, Class clazz) {
        // XStream对象设置默认安全防护，同时设置允许的类
        XStream stream = new XStream(new DomDriver());
        XStream.setupDefaultSecurity(stream);
        stream.allowTypes(new Class[]{clazz});
        stream.processAnnotations(new Class[]{clazz});
        stream.setMode(XStream.NO_REFERENCES);
        stream.alias("xml", clazz);
        return (T) stream.fromXML(resultXml);
    }
}
